#include "wavelet.h"

Wavelet::Wavelet(int h, int w, int l) :
    height(h), width(w), level(l)
{

}

Wavelet::~Wavelet()
{

}

void Wavelet::transform(float *data)
{
    int w1 = width;
    int h1 = height;

    // First level DWT
    DWT(data);

    for(int i = 1; i < level; i++)
    {
        // The rest levels of DWT
        // In this case, only 2 levels will be performed
        width = width / 2;
        height = height / 2;

        float *temp = new float[width*height];

        imgCopy(data, temp, w1);
        DWT(temp);
        imgReturn(temp, data, w1);

        delete [] temp;
    }

    width = w1;
    height = h1;
}

void Wavelet::DWT(float *line)
{
    int i, start, end;

    // x-direction DWT
    for(i = 0; i < height; i++)
    {
        start = width * i;
        end = start + width - 1;
        suband(line, start, end);
    }

    transpose(line);

    // y-direction DWT
    for(i = 0; i < height; i++)
    {
        start = width * i;
        end = start + width - 1;
        suband(line, start, end);
    }

    transpose(line);
}

void Wavelet::suband(float *line, int start, int end)
{
    int count;
    float hn[6], gn[6], hbar[6], gbar[6];
    float *line1 = new float[width];

    hn[0] = 0.332670552950;
    hn[1] = 0.806891509311;
    hn[2] = 0.459877502118;
    hn[3] = -0.135011020010;
    hn[4] = -0.085441273882;
    hn[5] = 0.035226291882;

    gn[0] = hn[5];
    gn[1] = -1*hn[4];
    gn[2] = hn[3];
    gn[3] = -1*hn[2];
    gn[4] = hn[1];
    gn[5] = -1*hn[0];

    for(count = 0; count < 6; count++)
    {
        hbar[count] = hn[5 - count];
        gbar[count] = gn[5 - count];
    }

    for(count = 0; count < width; count++)
    {
        *(line1 + count) = *(line + start + count);
    }

    convolution(line, hbar, start, end);
    convolution(line1, gbar, 0, width-1);

    downSample(line, start);
    downSample(line1, 0);

    // concat line and line1 together
    for(count = 0; count < width/2; count++)
    {
        *(line + start + count + width/2) = *(line1 + count);
    }

    delete [] line1;
}

void Wavelet::convolution(float *line, float *coe, int start, int end)
{
    int i, j;

    float *wav = new float[width];

    for(i = 0; i < width; i++)
        *(wav + i) = 0.0;

    // Circular convolution
    for(i = 0; i < 6; i++)
    {
        for(j = 0; j < 6; j++)
        {
            if((i - j) < 0)
                *(wav + i) = *(wav + i) + *(line + start + width + i - j) * coe[j];
            else
                *(wav + i) = *(wav + i) + *(line + start + i - j) * coe[j];
        }
    }

    for(i = 6; i < width; i++)
        for(j = 0; j < 6; j++)
            *(wav + i) = *(wav + i) + *(line + start + i - j) * coe[j];

    // Store result back to original array
    for(i = 0; i < width; i++)
        *(line + start + i) = *(wav + i);

    delete [] wav;
}

void Wavelet::downSample(float *line, int start)
{
    int i;

    // store every other item into first half
    for(i = 0; i < width/2; i++)
        *(line + start + i) = *(line + start + i*2 + 1);

    // set next half of the data to zero
    for(i = 0; i < width/2; i++)
        *(line + i + width/2) = 0.0;
}

void Wavelet::imgCopy(float *src, float *dst, int w)
{
    int i, j;

    for(i = 0; i < height; i++)
    {
        for(j = 0; j < width; j++)
        {
            *(dst + i*width + j) = *(src + i*w + j);
        }
    }
}

void Wavelet::imgReturn(float *src, float *dst, int w)
{
    int i, j;

    for(i = 0; i < height; i++)
    {
        for(j = 0; j < width; j++)
        {
            *(dst + i*w + j) = *(src + i*width + j);
        }
    }
}

void Wavelet::transpose(float *line)
{
    int i, j;
    int temp;
    float *line1 = new float[height*width];

    for(i = 0; i < height; i++)
        for(j = 0; j < width; j++)
            *(line1+j*height+i) = *(line+i*width+j);

    for(i = 0; i < height*width; i++)
        *(line+i) = *(line1+i);

    temp = width;
    width = height;
    height = temp;

    delete [] line1;
}
///////////////////////////////////
// End of wavelet transformation //
///////////////////////////////////
